/*
 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <Python.h>
#include "hand_classify.h"
#include "sample_comm_nnie.h"
#include "sample_media_ai.h"
#include "ai_infer_process.h"
#include "yolov2_hand_detect.h"
#include "vgs_img.h"
#include "ive_img.h"
#include "misc_util.h"
#include "hisignalling.h"

#ifdef __cplusplus
#if __cplusplus
extern "C"
{
#endif
#endif /* End of #ifdef __cplusplus */
#define HAND_FRM_WIDTH 640
#define HAND_FRM_HEIGHT 384
#define DETECT_OBJ_MAX 32
#define RET_NUM_MAX 4
#define DRAW_RETC_THICK 6 // Draw the width of the line
#define WIDTH_LIMIT 32
#define HEIGHT_LIMIT 32
#define IMAGE_WIDTH 224 // The resolution of the model IMAGE sent to the classification is 224*224
#define IMAGE_HEIGHT 224
#define MODEL_FILE_GESTURE "/userdata/models/hand_classify/mx.wk" // darknet framework wk model

    static int biggestBoxIndex;
    static IVE_IMAGE_S img;
    static DetectObjInfo objs[DETECT_OBJ_MAX] = {0};
    static RectBox boxs[DETECT_OBJ_MAX] = {0};
    static RectBox objBoxs[DETECT_OBJ_MAX] = {0};
    static RectBox leftearBoxs[DETECT_OBJ_MAX] = {0};
    static RectBox rightearBoxs[DETECT_OBJ_MAX] = {0};
    static RectBox mouthBoxs[DETECT_OBJ_MAX] = {0};
    static RectBox remainingBoxs[DETECT_OBJ_MAX] = {0};
    static RectBox cnnBoxs[DETECT_OBJ_MAX] = {0}; // Store the results of the classification network
    static RecogNumInfo numInfo[RET_NUM_MAX] = {0};
    static IVE_IMAGE_S imgIn;
    static IVE_IMAGE_S imgDst;
    static VIDEO_FRAME_INFO_S frmIn;
    static VIDEO_FRAME_INFO_S frmDst;
    int uartFd = 0;

    HI_S32 Yolo2FaceDetectResnetClassifyLoad(uintptr_t *model)
    {
        SAMPLE_SVP_NNIE_CFG_S *self = NULL;
        HI_S32 ret = 0;

             ret = CnnCreate(&self, MODEL_FILE_GESTURE);
        *model = ret < 0 ? 0 : (uintptr_t)self;

        *model = 1;
        HandDetectInit(); // Initialize the hand detection model
        SAMPLE_PRT("Load hand detect claasify model success\n");
        /* uart open init */

        uartFd = UartOpenInit(); 
        if (uartFd < 0)
        {
            printf("uart1 open failed\r\n");
        }
        else
        {
            printf("uart1 open successed\r\n");
        }
        return ret;
    }
    HI_S32 Yolo2FaceDetectResnetClassifyUnload(uintptr_t model)
    {
        CnnDestroy((SAMPLE_SVP_NNIE_CFG_S *)model);
        FaceDetectExit(); // Uninitialize the face detection model
        SAMPLE_PRT("Unload hand detect claasify model success\n");

        return 0;
    }

    /* Get the maximum hand */
    static HI_S32 GetBigFaceIndex(RectBox boxs[], int detectNum)
    {
        HI_S32 faceIndex = 0;
        HI_S32 biggestBoxIndex = faceIndex;
        HI_S32 biggestBoxWidth = boxs[faceIndex].xmax - boxs[faceIndex].xmin + 1;
        HI_S32 biggestBoxHeight = boxs[faceIndex].ymax - boxs[faceIndex].ymin + 1;
        HI_S32 biggestBoxArea = biggestBoxWidth * biggestBoxHeight;

        for (faceIndex = 1; faceIndex < detectNum; faceIndex++)
        {
            HI_S32 boxWidth = boxs[faceIndex].xmax - boxs[faceIndex].xmin + 1;
            HI_S32 boxHeight = boxs[faceIndex].ymax - boxs[faceIndex].ymin + 1;
            HI_S32 boxArea = boxWidth * boxHeight;
            if (biggestBoxArea < boxArea)
            {
                biggestBoxArea = boxArea;
                biggestBoxIndex = faceIndex;
            }
            biggestBoxWidth = boxs[biggestBoxIndex].xmax - boxs[biggestBoxIndex].xmin + 1;
            biggestBoxHeight = boxs[biggestBoxIndex].ymax - boxs[biggestBoxIndex].ymin + 1;
        }

        if ((biggestBoxWidth == 1) || (biggestBoxHeight == 1) || (detectNum == 0))
        {
            biggestBoxIndex = -1;
        }

        return biggestBoxIndex;
    }

    /* hand gesture recognition info */
    static void faceDetectFlag(const RecogNumInfo resBuf)
    {
        HI_CHAR *gestureName = NULL;
        switch (resBuf.num)
        {
        case 0u:
            gestureName = "gesture fist";
            // 发给3861
            UartSendRead(uartFd, maskGesture); //口罩
            SAMPLE_PRT("----gesture name----:%s\n", gestureName);
            break;
        case 1u:
            gestureName = "gesture indexUp";
            UartSendRead(uartFd, wheelGesture); //方向盘
            SAMPLE_PRT("----gesture name----:%s\n", gestureName);
            break;
        case 2u:
            gestureName = "gesture OK";
            UartSendRead(uartFd, phoneGesture); //手机
            SAMPLE_PRT("----gesture name----:%s\n", gestureName);
            break;
        case 3u:
            gestureName = "gesture OK";
            UartSendRead(uartFd, bleltGesture); //安全带
            SAMPLE_PRT("----gesture name----:%s\n", gestureName);
            break;
        default:
            gestureName = "gesture others";
            UartSendRead(uartFd, InvalidGesture); // 无效值
            SAMPLE_PRT("----gesture name----:%s\n", gestureName);
            break;
        }
        SAMPLE_PRT("face gesture success\n");
    }

    HI_S32 Yolo2HandDetectResnetClassifyCal(uintptr_t model, VIDEO_FRAME_INFO_S *srcFrm, VIDEO_FRAME_INFO_S *dstFrm)
    {
        SAMPLE_SVP_NNIE_CFG_S *self = (SAMPLE_SVP_NNIE_CFG_S *)model;
        HI_S32 resLen = 0;
        int objNum;
        int ret;
        int num = 0;

        ret = FrmToOrigImg((VIDEO_FRAME_INFO_S *)srcFrm, &img);
        SAMPLE_CHECK_EXPR_RET(ret != HI_SUCCESS, ret, "hand detect for YUV Frm to Img FAIL, ret=%#x\n", ret);

        objNum = HandDetectCal(&img, objs); // Send IMG to the detection net for reasoning
        //坐标转换
        for (int i = 0; i < objNum; i++)
        {
            cnnBoxs[i] = objs[i].box;
            RectBox *box = &objs[i].box;
            RectBoxTran(box, HAND_FRM_WIDTH, HAND_FRM_HEIGHT,
                        dstFrm->stVFrame.u32Width, dstFrm->stVFrame.u32Height);
            SAMPLE_PRT("yolo2_out: {%d, %d, %d, %d}\n", box->xmin, box->ymin, box->xmax, box->ymax);
            boxs[i] = *box;
        }
           // When an object is detected, a rectangle is drawn in the DSTFRM
        if (biggestBoxIndex >= 0)
        {
            objBoxs[0] = boxs[biggestBoxIndex];
            MppFrmDrawRects(dstFrm, objBoxs, 1, RGB888_GREEN, DRAW_RETC_THICK); // Target hand objnum is equal to 1

            for (int j = 0; (j < objNum) && (objNum > 1); j++)
            {
                if (j != biggestBoxIndex)
                {
                    remainingBoxs[num++] = boxs[j];
                    // others hand objnum is equal to objnum -1
                    MppFrmDrawRects(dstFrm, remainingBoxs, objNum - 1, RGB888_RED, DRAW_RETC_THICK);
                }
            }

            // Crop the image to classification network
            ret = ImgYuvCrop(&img, &imgIn, &cnnBoxs[biggestBoxIndex]);
            SAMPLE_CHECK_EXPR_RET(ret < 0, ret, "ImgYuvCrop FAIL, ret=%#x\n", ret);

            if ((imgIn.u32Width >= WIDTH_LIMIT) && (imgIn.u32Height >= HEIGHT_LIMIT))
            {
                COMPRESS_MODE_E enCompressMode = srcFrm->stVFrame.enCompressMode;
                ret = OrigImgToFrm(&imgIn, &frmIn);
                frmIn.stVFrame.enCompressMode = enCompressMode;
                SAMPLE_PRT("crop u32Width = %d, img.u32Height = %d\n", imgIn.u32Width, imgIn.u32Height);
                ret = MppFrmResize(&frmIn, &frmDst, IMAGE_WIDTH, IMAGE_HEIGHT);
                ret = FrmToOrigImg(&frmDst, &imgDst);
                ret = CnnCalU8c1Img(self, &imgDst, numInfo, sizeof(numInfo) / sizeof((numInfo)[0]), &resLen);
                SAMPLE_CHECK_EXPR_RET(ret < 0, ret, "CnnCalU8c1Img FAIL, ret=%#x\n", ret);
                HI_ASSERT(resLen <= sizeof(numInfo) / sizeof(numInfo[0]));
                HandDetectFlag(numInfo[0]);
                MppFrmDestroy(&frmDst);
            }
            IveImgDestroy(&imgIn);
        }

        return ret;
    }

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */
